package com.ymx.darling.exception;

import com.ymx.darling.executor.mapping.relation.RelationType;
import com.ymx.darling.session.setting.Context;
import com.ymx.darling.util.ObjectUtil;
import com.ymx.darling.util.StringUtil;

import java.lang.reflect.Method;
import java.util.List;
import java.util.StringJoiner;

/**
 * 异常的工厂
 * @author 爱Java的小于
 */
public class ExceptionFactory {
    /**
     * 封装其他异常信息封装
     * @param reason 报错原因
     * @param exception cheetahException
     * @return DarlingException
     */
    public static DarlingException wrapException(String reason, Exception exception) {
        if (isDarlingExceptionClass(exception)) {
            return (DarlingException) exception;
        } else {
            Throwable throwable = getTargetException(exception);
            String errContext = "\n" + String.format("####### %s\n", reason) +
                    getThrowExceptionWithMethod() +
                    String.format("####### 异常类型:%s\n", throwable.getClass().getName()) +
                    String.format("####### 异常信息:%s", exception.getMessage());
            return new DarlingException(throwable, errContext);
        }
    }


    /**
     * 扫描mapper时抛出的异常
     * @param mapper mapper
     * @param exception e
     * @return e
     */
    public static DarlingException wrapMapperException(Class<?> mapper, Exception exception) {
        if (isDarlingExceptionClass(exception)) {
            return (DarlingException) exception;
        } else {
            Throwable throwable = getTargetException(exception);
            String errContext = "\n" + "####### 解析mapper时抛出异常\n" +
                    String.format("####### mapper:%s\n", mapper.getName()) +
                    String.format("####### 异常类型:%s\n", throwable.getClass().getName()) +
                    String.format("####### 异常信息:%s", exception.getMessage());
            return new DarlingException(throwable, errContext);
        }
    }



    /**
     * 解析、执行 sql时抛出的异常
     * @param title 异常标题 指明抛出异常的阶段 解析sql或执行sql
     * @param sql sql
     * @param params 参数
     * @param exception 具体的异常信息
     * @return DarlingException
     */
    public static DarlingException wrapSqlHandleException(String title, String sql, List<Object> params, Exception exception) {
        if (isDarlingExceptionClass(exception)) {
            return (DarlingException) exception;
        } else {
            StringJoiner joiner = new StringJoiner(", ");
            // 判断参数不为数组类型 则将参数转换为数组类型
            params.forEach(param -> {
                if (param == null) {
                    joiner.add("null");
                } else {
                    String paramValue = StringUtil.toString(param);
                    joiner.add(paramValue + "(" + ObjectUtil.getSimpleName(param.getClass()) + ")");
                }
            });

            Throwable throwable = getTargetException(exception);
            String errContext = String.format("\n####### %s\n", title) +
                    getThrowExceptionWithMethod() +
                    String.format("####### SQL:%s\n", sql) +
                    String.format("####### 参数:%s\n", joiner) +
                    String.format("####### 异常类型:%s\n", throwable.getClass().getName()) +
                    String.format("####### 异常信息:%s", exception.getMessage());
            return new DarlingException(throwable, errContext);
        }
    }


    /**
     * 处理结果集时抛出异常
     * @param module 实体映射类型
     * @param exception 异常类型
     * @return DarlingException
     */
    public static DarlingException wrapResultMappingException(Class<?> module, Exception exception) {
        if (isDarlingExceptionClass(exception)) {
            return (DarlingException) exception;
        } else {
            Throwable throwable = getTargetException(exception);
            String errContext = "\n####### 结果集映射实体类时抛出异常\n" +
                    getThrowExceptionWithMethod() +
                    String.format("####### 映射实体:%s\n", module.getName()) +
                    String.format("####### 异常类型:%s\n", throwable.getClass().getName()) +
                    String.format("####### 异常信息:%s", exception.getMessage());
            throw new DarlingException(throwable, errContext);
        }
    }


    /**
     * 查询关联数据时相关异常的包装
     * @param module 关联的数据类型
     * @param fieldName 属性名
     * @param relationType 关联类型
     * @param exception 异常类型
     * @return DarlingException
     */
    public static DarlingException wrapRelationException(String module, String fieldName, RelationType relationType, Exception exception) {
        if (isDarlingExceptionClass(exception)) {
            return (DarlingException) exception;
        } else {
            Throwable throwable = getTargetException(exception);
            String errContext = "\n####### 加载关联字段时抛出异常\n" +
                    getThrowExceptionWithMethod() +
                    String.format("####### 字段:%s.%s\n", module, fieldName) +
                    String.format("####### 关联关系:%s\n", relationType) +
                    String.format("####### 异常类型:%s\n", throwable.getClass().getName()) +
                    String.format("####### 异常信息:%s", exception.getMessage());
            return new DarlingException(throwable, errContext);
        }
    }


    public static DarlingException wrapEntityException(Class<?> entity, Exception exception) {
        if (isDarlingExceptionClass(exception)) {
            return (DarlingException)exception;
        } else {
            Throwable throwable = getTargetException(exception);
            String errContext = "\n####### 解析实体类时抛出异常\n" +
                    String.format("####### 实体:%s\n", entity.getName()) +
                    String.format("####### 异常类型:%s\n", throwable.getClass().getName()) +
                    String.format("####### 异常信息:%s", exception.getMessage());
            return new DarlingException(throwable, errContext);
        }
    }


    /**
     * 实体填充数据时的异常
     * @param entity 填充的实体类型
     * @param errTitle 异常标题
     * @param exception 异常实例
     * @return DarlingException
     */
    public static DarlingException fillEntityException(Class<?> entity, String errTitle, Exception exception) {
        Throwable targetException = getTargetException(exception);
        String errInfo = exception.getMessage();
        String errContext = String.format("\n####### %s\n", errTitle) +
                getThrowExceptionWithMethod() +
                String.format("####### 实体:%s\n", entity.getName()) +
                String.format("####### 异常类型:%s%n", targetException.getClass().getName()) +
                String.format("####### 异常信息:%s", errInfo);
        return new EntityException(exception, errContext);
    }


    /**
     * 返回异常由那个方法抛出
     * @return 抛出异常的mapper方法名
     */
    private static String getThrowExceptionWithMethod() {
        if (Context.hasNamespace() && Context.hasMapperMethod()) {
            Class<?> namespace = Context.getNamespace();
            Method mapperMethod = Context.getMapperMethod();
            return String.format("####### 异常在调用方法'%s.%s'时发生\n",namespace.getName(), mapperMethod.getName());
        }
        if (Context.hasSqlStatementId()) {
            return String.format("####### 异常SQL的id:'%s'\n",Context.getSqlStatementId());
        }
        return "";
    }


    /**
     * 判断是否是DarlingException的Class
     * @param darlingException darlingException
     * @return boolean
     */
    private static boolean isDarlingExceptionClass(Exception darlingException) {
        return darlingException.getClass().equals(DarlingException.class);
    }


    /**
     * 返回目标异常
     * @param exception exception
     * @return throwable
     */
    private static Throwable getTargetException(Exception exception) {
        return exception instanceof ExceptionWrapper ? ((ExceptionWrapper)exception).getThrowable() : exception;
    }


    /**
     * 异常抛出策略
     * @param one 优先判断抛出此异常
     * @param two 其次抛出该异常
     */
    public static RuntimeException throwStrategy(Exception one, RuntimeException two) {
        /*
         * 优先判断第一个参数异常类型是否是直接抛出
         */
        return isDarlingExceptionClass(one) ? (DarlingException)one : two;
    }
}
